Golang string []byte 高性能转换
前言
有时候经常要在golang用到byte与string的转换,然而在golang中通过string()和[]byte()的转换是需要copy的,通过benchmark就可以看出这样的转换存在一定的开销对gc也是不友好的。
1 | func Benchmark_str2bytesByNormal(b *testing.B) { |
1 | goos: windows |
可以看到上面的benchmark一个op的耗时为7.38ns
查看string源码
golang的string实现在src/runtime/string.go,stringStruct即是string类型的本尊。len根据望文生义,就是string的长度了。这个str其实是个指向具体数据数组的指针。
1 | type stringStruct struct { |
那么要如何探索呢? 这里可以用到unsafe.Pointer然后强制转型为在我们可访问包里的stringStruct,然后通过goland打个断点观察。
可以看到len确实是string对应的长度,由于str是个unsafe.Pointer,所以goland也无法直接查看了。
由于是指向数组的指针,我们可以利用地址偏移的方式进行打印,从而来验证我们的结论。
1 | func main() { |
通过运行可以看到,确实输出了预想的内容
slice源码
slice的源码同样在src/runtime下,slice.go即是slice的源码所在之处,文件开头的结构体即是slice的本尊。
1 | type slice struct { |
其实他的array跟stringstruct里的是一模一样的东西,通过类似的代码可以再次验证
通过观察我们可以得出string跟[]byte的结构在内存上其实十分相似,只不过byte slice比string多了一个cap,也就是容量。到这里,我们就可以通过组装的方式来实现string跟[]byte的互转了。
也就是说string转[]byte,我们可以构造一块内存区域,在最后加上cap,这个cap取len的值即可,然后强制转换。
对于[]byte转string使用unsafe.Pointer后直接强制转换即可。
这样省去了对他们持有的数据的拷贝,提高了性能。
具体实现
1
2
3
4
5
6
7
8
9
10
func str2bytes(s string) []byte {
x := (*[2]uintptr)(unsafe.Pointer(&s))
h := [3]uintptr{x[0], x[1], x[1]}
return *(*[]byte)(unsafe.Pointer(&h))
}
func bytes2str(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
性能对比
通过对str2bytes进行benchmark,结果如下。可见每次op的时间显著减小。
1 | goos: windows |
Golang string []byte 高性能转换
https://www.huihongcloud.com/2020/06/17/go/Golang zero copy string bytes fast conversion/